package io.github.yangyouwang.module.mm.service;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import io.github.yangyouwang.module.mm.entity.FutureSymbolCurrentPrice;
import io.github.yangyouwang.module.mm.entity.GridAndFixRatioSymbolInfo;
import io.github.yangyouwang.module.mm.entity.KLine;
import io.github.yangyouwang.module.mm.entity.SymbolExtInfo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.util.*;

@Service
public class GridAndFixRatioService {

    private static final Logger logger = LoggerFactory.getLogger(GridAndFixRatioService.class);

    /**
     * 使用过去多少年的数据来计算(不包括今年)
     */
    public static final int HISTORY_YEAR_COUNT = 100;

    /**
     * 需要多少个网格
     */
    public static final int PARAM_GRID_COUNT = 8;
    /**
     * 网格开仓最多允许亏损比例
     */
    public static final double PARAM_MAX_LOSS_RATIO = 0.4;
    /**
     * 持仓均价盈利多少的比例后平仓
     */
    public static final double PARAM_POSITION_PROFIT_RATIO = 0.08;
    /**
     * 允许同时持仓的品种数
     */
    public static final int PARAM_MAX_POSITION_SYMBOL_COUNT = 3;
    /**
     * 强制指定最小跌的比例
     */
    public static final double PARAM_MIN_FALL_RATIO = 0.25;

    /**
     * 允许开仓的品种列表
     */
    public static final List<String> allowExchangeSymbols = Lists.newArrayList(
            "cu", "al", "zn", "pb", "ni", "sn", "au", "ag", "rb", "hc", "ss", "fu", "bu", "ru", "sp",
            "i", "j", "jm", "m", "y", "a", "b", "p", "c", "cs", "jd", "lh", "l", "v", "eg", "pp", "eb", "pg",
            "TA", "MA", "SA", "UR", "PF", "FG", "SR", "CF", "OI", "PK", "SF", "SM", "AP", "CJ", "RM",
            "sc", "lu", "nr", "bc", "IF", "IH", "IC", "IM", "TS", "TF", "T", "si");

    public static final Map<String, SymbolExtInfo> symbol2ExtInfoMap = new HashMap<>();

    {
        symbol2ExtInfoMap.put("cu",
                new SymbolExtInfo("1996-04-02", Lists.newArrayList(
                        new KLine("1996", 25530, 26380, 18620, 20640),
                        new KLine("1997", 20750, 24320, 16400, 17090),
                        new KLine("1998", 17000, 18900, 14910, 15070),
                        new KLine("1999", 15130, 18820, 14300, 18790),
                        new KLine("2000", 18840, 20080, 17470, 18490),
                        new KLine("2001", 18410, 18430, 13670, 15160),
                        new KLine("2002", 15170, 17070, 14730, 15960),
                        new KLine("2003", 15990, 23400, 15890, 23380),
                        new KLine("2004", 23400, 30780, 22970, 29950),
                        new KLine("2005", 29110, 42260, 27930, 41720),
                        new KLine("2006", 41950, 85500, 41950, 60520),
                        new KLine("2007", 57950, 75810, 49680, 56880),
                        new KLine("2008", 57000, 70550, 22210, 23840),
                        new KLine("2009", 24810, 60020, 24530, 60020),
                        new KLine("2010", 60150, 72770, 48800, 72380),
                        new KLine("2011", 72400, 76950, 50760, 55360),
                        new KLine("2012", 56450, 62080, 52330, 57860),
                        new KLine("2013", 58610, 60270, 47560, 52280),
                        new KLine("2014", 52290, 52700, 43690, 45800),
                        new KLine("2015", 45500, 46390, 33220, 36650),
                        new KLine("2016", 36540, 49660, 34350, 45500)
                )));
        symbol2ExtInfoMap.put("al",
                new SymbolExtInfo("1994-10-07", Lists.newArrayList(
                        new KLine("1994", 16250, 19240, 15900, 18360),
                        new KLine("1995", 18340, 20750, 16250, 16730),
                        new KLine("1996", 16730, 16750, 12900, 13950),
                        new KLine("1997", 13910, 16880, 13200, 14090),
                        new KLine("1998", 13960, 14440, 12550, 13900),
                        new KLine("1999", 13900, 16600, 12770, 16540),
                        new KLine("2000", 16560, 17380, 15010, 15080),
                        new KLine("2001", 15180, 15520, 12930, 13430),
                        new KLine("2002", 13490, 14370, 12900, 13590),
                        new KLine("2003", 13530, 16440, 13470, 16300),
                        new KLine("2004", 16310, 19430, 14950, 16570),
                        new KLine("2005", 16540, 19870, 15600, 19360),
                        new KLine("2006", 19420, 24830, 18210, 20550),
                        new KLine("2007", 20040, 20900, 17320, 18180),
                        new KLine("2008", 18120, 21495, 10125, 11540),
                        new KLine("2009", 11900, 17405, 11105, 17390),
                        new KLine("2010", 17440, 18685, 14000, 16840),
                        new KLine("2011", 16835, 18645, 15600, 15845),
                        new KLine("2012", 15990, 16460, 14925, 15365),
                        new KLine("2013", 15395, 15420, 13875, 14035),
                        new KLine("2014", 14010, 15010, 12635, 13060),
                        new KLine("2015", 12970, 13620, 9620, 10820),
                        new KLine("2016", 10850, 14850, 10500, 12795)
                )));
        symbol2ExtInfoMap.put("zn",
                new SymbolExtInfo("2007-03-26", Lists.newArrayList(
                        new KLine("2007", 28600, 35590, 16530, 18950),
                        new KLine("2008", 18940, 23275, 8380, 10120),
                        new KLine("2009", 10550, 21425, 9880, 21375),
                        new KLine("2010", 21405, 22600, 13480, 19695),
                        new KLine("2011", 19895, 20560, 13780, 14795),
                        new KLine("2012", 14945, 16360, 14145, 15520),
                        new KLine("2013", 15545, 16255, 14025, 15195),
                        new KLine("2014", 15190, 17480, 14620, 16780),
                        new KLine("2015", 16710, 17470, 11815, 13415),
                        new KLine("2016", 13385, 25060, 12335, 20995)
                )));
        symbol2ExtInfoMap.put("pb",
                new SymbolExtInfo("2011-03-24", Lists.newArrayList(
                        new KLine("2011", 19230, 19570, 13590, 15285),
                        new KLine("2012", 15405, 16700, 14190, 15265),
                        new KLine("2013", 15315, 15600, 13405, 14275),
                        new KLine("2014", 14235, 15510, 11800, 12380),
                        new KLine("2015", 12330, 14025, 11770, 13170),
                        new KLine("2016", 13130, 22940, 12520, 17665)
                )));
        symbol2ExtInfoMap.put("ni",
                new SymbolExtInfo("2015-03-27", Lists.newArrayList(
                        new KLine("2015", 101870, 115300, 63310, 70630),
                        new KLine("2016", 70810, 99800, 64280, 85560)
                )));
        symbol2ExtInfoMap.put("sn",
                new SymbolExtInfo("2015-03-27", Lists.newArrayList(
                        new KLine("2015", 120000, 125910, 80000, 93370),
                        new KLine("2016", 92680, 157310, 90110, 146820)
                )));
        symbol2ExtInfoMap.put("au",
                new SymbolExtInfo("2008-07-15", Lists.newArrayList(
                        new KLine("2008", 212.88, 214.2, 148.89, 189.21),
                        new KLine("2009", 191.03, 270.49, 176, 247.51),
                        new KLine("2010", 245.38, 314, 234.88, 309.11),
                        new KLine("2011", 308.79, 398.08, 283.28, 319.45),
                        new KLine("2012", 328.98, 366.3, 313.72, 340.29),
                        new KLine("2013", 337.19, 345.6, 237.9, 239.4),
                        new KLine("2014", 241.1, 273.4, 223.1, 241.65),
                        new KLine("2015", 240, 263.2, 216.8, 226.05),
                        new KLine("2016", 226.7, 297.7, 226.2, 271.35)
                )));
        symbol2ExtInfoMap.put("ag",
                new SymbolExtInfo("2012-05-10", Lists.newArrayList(
                        new KLine("2012", 6190, 7506, 5632, 6427),
                        new KLine("2013", 6306, 6715, 3710, 4099),
                        new KLine("2014", 4118, 4483, 3216, 3522),
                        new KLine("2015", 3498, 3934, 3150, 3290),
                        new KLine("2016", 3302, 4599, 3250, 4113)
                )));
        symbol2ExtInfoMap.put("rb",
                new SymbolExtInfo("2009-03-27", Lists.newArrayList(
                        new KLine("2009", 3550, 4982, 3410, 4511),
                        new KLine("2010", 4523, 4906, 3886, 4812),
                        new KLine("2011", 4830, 5230, 3838, 4210),
                        new KLine("2012", 4235, 4405, 3206, 3988),
                        new KLine("2013", 4018, 4288, 3382, 3570),
                        new KLine("2014", 3562, 2592, 2417, 2594),
                        new KLine("2015", 2550, 2647, 1618, 1784),
                        new KLine("2016", 1821, 3557, 1732, 2867)
                )));
        symbol2ExtInfoMap.put("hc",
                new SymbolExtInfo("2014-03-21", Lists.newArrayList(
                        new KLine("2014", 3252, 3488, 2754, 2928),
                        new KLine("2015", 2928, 2992, 1675, 1949),
                        new KLine("2016", 1980, 3898, 1850, 3305)
                )));
        symbol2ExtInfoMap.put("ss",
                new SymbolExtInfo("2019-09-25", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("fu",
                new SymbolExtInfo("2018-07-16", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("bu",
                new SymbolExtInfo("2013-10-09", Lists.newArrayList(
                        new KLine("2013", 4598, 4658, 4228, 4304),
                        new KLine("2014", 4302, 4576, 2984, 3508),
                        new KLine("2015", 3498, 3666, 1642, 1894),
                        new KLine("2016", 1918, 2778, 1620, 2738)
                )));
        symbol2ExtInfoMap.put("ru",
                new SymbolExtInfo("1997-04-14", Lists.newArrayList(
                        new KLine("1997", 11180, 11800, 7190, 7260),
                        new KLine("1998", 7320, 9900, 6900, 9500),
                        new KLine("1999", 9480, 10300, 7530, 9405),
                        new KLine("2000", 9470, 10030, 8030, 8190),
                        new KLine("2001", 8190, 8195, 6320, 6660),
                        new KLine("2002", 6670, 11025, 6580, 10970),
                        new KLine("2003", 10970, 17580, 9940, 15550),
                        new KLine("2004", 15580, 17485, 11870, 12055),
                        new KLine("2005", 12040, 18600, 11890, 18405),
                        new KLine("2006", 18425, 29980, 16605, 19810),
                        new KLine("2007", 19380, 24360, 17305, 23185),
                        new KLine("2008", 23200, 28495, 8650, 10960),
                        new KLine("2009", 11455, 24530, 11455, 24460),
                        new KLine("2010", 24595, 38920, 20490, 37190),
                        new KLine("2011", 37470, 43500, 23870, 24160),
                        new KLine("2012", 24550, 29580, 20700, 26510),
                        new KLine("2013", 26530, 27040, 16825, 18140),
                        new KLine("2014", 18195, 18265, 11580, 13590),
                        new KLine("2015", 13520, 15270, 9680, 10540),
                        new KLine("2016", 10585, 20580, 9590, 18350)
                )));
        symbol2ExtInfoMap.put("sp",
                new SymbolExtInfo("2018-11-27", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("i",
                new SymbolExtInfo("2013-10-18", Lists.newArrayList(
                        new KLine("2013", 978, 984, 885, 911),
                        new KLine("2014", 912, 912, 458, 509),
                        new KLine("2015", 505, 521, 282.5, 322),
                        new KLine("2016", 325.5, 658.5, 298, 548)
                )));
        symbol2ExtInfoMap.put("j",
                new SymbolExtInfo("2011-04-15", Lists.newArrayList(
                        new KLine("2011", 2300, 2444, 1888, 2000),
                        new KLine("2012", 2018, 2124, 1288, 1824),
                        new KLine("2013", 1835, 2062, 1398, 1458),
                        new KLine("2014", 1456, 1465, 996, 1032),
                        new KLine("2015", 1029, 1066, 600, 643),
                        new KLine("2016", 647, 2276.5, 610, 1518)
                )));
        symbol2ExtInfoMap.put("jm",
                new SymbolExtInfo("2013-03-22", Lists.newArrayList(
                        new KLine("2013", 1280, 1304, 989, 1005),
                        new KLine("2014", 1003, 1012, 706, 740),
                        new KLine("2015", 736, 756, 484.5, 565),
                        new KLine("2016", 567, 1676, 520.5, 1177)
                )));
        symbol2ExtInfoMap.put("m",
                new SymbolExtInfo("2000-07-17", Lists.newArrayList(
                        new KLine("2000", 2160, 2160, 1106, 1944),
                        new KLine("2001", 1820, 1960, 1511, 1591),
                        new KLine("2002", 1585, 2056, 1554, 2042),
                        new KLine("2003", 2048, 2980, 1880, 2827),
                        new KLine("2004", 2835, 3789, 2130, 2178),
                        new KLine("2005", 2150, 2903, 2073, 2326),
                        new KLine("2006", 2380, 2556, 2170, 2400),
                        new KLine("2007", 2360, 3585, 2305, 3412),
                        new KLine("2008", 3399, 4361, 2113, 2479),
                        new KLine("2009", 2500, 3302, 2391, 3020),
                        new KLine("2010", 3050, 3588, 2683, 3440),
                        new KLine("2011", 3439, 3604, 2738, 2951),
                        new KLine("2012", 2972, 4369, 2870, 3276),
                        new KLine("2013", 3240, 3782, 3051, 3368),
                        new KLine("2014", 3346, 3876, 2801, 2869),
                        new KLine("2015", 2802, 2881, 2284, 2380),
                        new KLine("2016", 2370, 3577, 2287, 2803)
                )));
        symbol2ExtInfoMap.put("y",
                new SymbolExtInfo("2006-01-19", Lists.newArrayList(
                        new KLine("2006", 5300, 6948, 4970, 6812),
                        new KLine("2007", 6700, 10538, 6362, 10474),
                        new KLine("2008", 10500, 14630, 5560, 6070),
                        new KLine("2009", 6220, 8198, 5818, 7968),
                        new KLine("2010", 8030, 10662, 7154, 10526),
                        new KLine("2011", 10520, 10976, 8580, 8994),
                        new KLine("2012", 9088, 10308, 8322, 8612),
                        new KLine("2013", 8698, 8932, 6842, 6850),
                        new KLine("2014", 6866, 7246, 5540, 5744),
                        new KLine("2015", 5670, 5988, 5180, 5724),
                        new KLine("2016", 5716, 7348, 5500, 6980)
                )));
        symbol2ExtInfoMap.put("a",
                new SymbolExtInfo("2002-03-18", Lists.newArrayList(
                        new KLine("2002", 2111, 2554, 2094, 2546),
                        new KLine("2003", 2556, 3499, 2381, 3378),
                        new KLine("2004", 3399, 4100, 2556, 2615),
                        new KLine("2005", 2585, 3255, 2505, 2692),
                        new KLine("2006", 2750, 2993, 2354, 2911),
                        new KLine("2007", 2851, 4740, 2800, 4726),
                        new KLine("2008", 4738, 5246, 2852, 3382),
                        new KLine("2009", 3409, 4130, 3263, 4038),
                        new KLine("2010", 4080, 4835, 3720, 4528),
                        new KLine("2011", 4528, 4828, 4134, 4340),
                        new KLine("2012", 4366, 5040, 4253, 4743),
                        new KLine("2013", 4720, 4918, 4263, 4441),
                        new KLine("2014", 4411, 4799, 4241, 4510),
                        new KLine("2015", 4488, 4548, 3580, 3620),
                        new KLine("2016", 3615, 4416, 3310, 4279)
                )));
        symbol2ExtInfoMap.put("b",
                new SymbolExtInfo("2004-12-22", Lists.newArrayList(
                        new KLine("2004", 2630, 2649, 2566, 2583),
                        new KLine("2005", 2570, 3143, 2415, 2686),
                        new KLine("2006", 2730, 3230, 2406, 3210),
                        new KLine("2007", 3063, 4729, 3050, 4712),
                        new KLine("2008", 4725, 5558, 2808, 3375),
                        new KLine("2009", 3380, 4390, 3305, 4085),
                        new KLine("2010", 4085, 4964, 3712, 4687),
                        new KLine("2011", 4650, 5220, 4462, 4735),
                        new KLine("2012", 4755, 5150, 4400, 4738),
                        new KLine("2013", 4691, 4754, 3891, 3933),
                        new KLine("2014", 3930, 4580, 3020, 3264),
                        new KLine("2015", 3300, 3358, 2688, 3358),
                        new KLine("2016", 3156, 4280, 3080, 3990)
                )));
        symbol2ExtInfoMap.put("p",
                new SymbolExtInfo("2007-10-29", Lists.newArrayList(
                        new KLine("2007", 8350, 9486, 8350, 9298),
                        new KLine("2008", 9300, 12992, 4152, 5002),
                        new KLine("2009", 5120, 7280, 4938, 7276),
                        new KLine("2010", 7300, 9890, 6152, 9786),
                        new KLine("2011", 9820, 10296, 7636, 8002),
                        new KLine("2012", 8100, 9034, 6482, 6922),
                        new KLine("2013", 6940, 7196, 5306, 6038),
                        new KLine("2014", 6080, 6546, 4892, 5078),
                        new KLine("2015", 5048, 5412, 3984, 4870),
                        new KLine("2016", 4850, 6550, 4540, 6202)
                )));
        symbol2ExtInfoMap.put("c",
                new SymbolExtInfo("2004-09-22", Lists.newArrayList(
                        new KLine("2004", 1230, 1235, 1116, 1156),
                        new KLine("2005", 1150, 1393, 1141, 1368),
                        new KLine("2006", 1390, 1664, 1353, 1636),
                        new KLine("2007", 1585, 1816, 1470, 1694),
                        new KLine("2008", 1695, 1990, 1440, 1548),
                        new KLine("2009", 1551, 1880, 1520, 1873),
                        new KLine("2010", 1878, 2419, 1841, 2330),
                        new KLine("2011", 2331, 2478, 2148, 2273),
                        new KLine("2012", 2282, 2497, 2271, 2433),
                        new KLine("2013", 2433, 2478, 2294, 2345),
                        new KLine("2014", 2344, 2439, 2287, 2380),
                        new KLine("2015", 2377, 2572, 1760, 1896),
                        new KLine("2016", 1895, 1981, 1382, 1519)
                )));
        symbol2ExtInfoMap.put("cs",
                new SymbolExtInfo("2014-12-19", Lists.newArrayList(
                        new KLine("2014", 2800, 2834, 2665, 2713),
                        new KLine("2015", 2718, 3100, 1890, 2054),
                        new KLine("2016", 2047, 2374, 1600, 1786)
                )));
        symbol2ExtInfoMap.put("jd",
                new SymbolExtInfo("2013-11-08", Lists.newArrayList(
                        new KLine("2013", 4050, 4196, 3932, 3951),
                        new KLine("2014", 3951, 5559, 3630, 4268),
                        new KLine("2015", 4255, 4587, 3141, 3209),
                        new KLine("2016", 3220, 4312, 2982, 3375)
                )));
        symbol2ExtInfoMap.put("lh",
                new SymbolExtInfo("2021-01-08", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("l",
                new SymbolExtInfo("2007-07-31", Lists.newArrayList(
                        new KLine("2007", 12780, 14235, 12250, 13665),
                        new KLine("2008", 13685, 16780, 5760, 7145),
                        new KLine("2009", 7475, 12520, 7340, 12495),
                        new KLine("2010", 12600, 14020, 9060, 12370),
                        new KLine("2011", 12510, 12845, 8470, 9605),
                        new KLine("2012", 9705, 11120, 9065, 10850),
                        new KLine("2013", 10900, 11650, 9470, 11100),
                        new KLine("2014", 11080, 12205, 8010, 8585),
                        new KLine("2015", 8540, 10400, 7080, 8040),
                        new KLine("2016", 8075, 10710, 7710, 9880)
                )));
        symbol2ExtInfoMap.put("v",
                new SymbolExtInfo("2009-05-25", Lists.newArrayList(
                        new KLine("2009", 6575, 8035, 6160, 7665),
                        new KLine("2010", 7720, 9650, 6790, 8375),
                        new KLine("2011", 8420, 8715, 6280, 6910),
                        new KLine("2012", 6980, 7240, 6250, 6620),
                        new KLine("2013", 6630, 6875, 6250, 6420),
                        new KLine("2014", 6410, 6535, 5270, 5445),
                        new KLine("2015", 5445, 5935, 4405, 4850),
                        new KLine("2016", 4850, 8205, 4570, 6210)
                )));
        symbol2ExtInfoMap.put("eg",
                new SymbolExtInfo("2018-12-10", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("pp",
                new SymbolExtInfo("2014-02-28", Lists.newArrayList(
                        new KLine("2014", 10680, 11532, 7540, 7972),
                        new KLine("2015", 7892, 9027, 5361, 5825),
                        new KLine("2016", 5880, 9857, 5525, 8800)
                )));
        symbol2ExtInfoMap.put("eb",
                new SymbolExtInfo("2019-09-26", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("pg",
                new SymbolExtInfo("2020-03-30", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("TA",
                new SymbolExtInfo("2006-12-18", Lists.newArrayList(
                        new KLine("2006", 8538, 9166, 8538, 8898),
                        new KLine("2007", 8690, 9070, 6866, 7342),
                        new KLine("2008", 7358, 9968, 4250, 5188),
                        new KLine("2009", 5358, 8460, 5070, 8138),
                        new KLine("2010", 8234, 11750, 6920, 10328),
                        new KLine("2011", 10430, 12396, 7700, 8410),
                        new KLine("2012", 8520, 9246, 6964, 8562),
                        new KLine("2013", 8602, 8946, 7322, 7362),
                        new KLine("2014", 7364, 7508, 4724, 4850),
                        new KLine("2015", 4824, 5634, 4214, 4462),
                        new KLine("2016", 4490, 5612, 4200, 5456)
                )));
        symbol2ExtInfoMap.put("MA",
                new SymbolExtInfo("2011-10-28", Lists.newArrayList(
                        new KLine("2011", 3199, 3220, 2661, 2731),
                        new KLine("2012", 2755, 3159, 2532, 2725),
                        new KLine("2013", 2729, 3595, 2573, 2995),
                        new KLine("2014", 2990, 3122, 1977, 2051),
                        new KLine("2015", 2030, 2654, 1590, 1736),
                        new KLine("2016", 1750, 3011, 1685, 2731)
                )));
        symbol2ExtInfoMap.put("SA",
                new SymbolExtInfo("2019-12-06", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("UR",
                new SymbolExtInfo("2019-08-09", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("PF",
                new SymbolExtInfo("2020-10-12", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("FG",
                new SymbolExtInfo("2012-12-03", Lists.newArrayList(
                        new KLine("2012", 1373, 1383, 1282, 1363),
                        new KLine("2013", 1371, 1652, 1278, 1290),
                        new KLine("2014", 1291, 1324, 848, 949),
                        new KLine("2015", 944, 985, 803, 831),
                        new KLine("2016", 833, 1390, 820, 1222)
                )));
        symbol2ExtInfoMap.put("SR",
                new SymbolExtInfo("2006-01-06", Lists.newArrayList(
                        new KLine("2006", 4700, 6246, 3210, 3773),
                        new KLine("2007", 3715, 4235, 3380, 3952),
                        new KLine("2008", 3933, 4844, 2789, 2852),
                        new KLine("2009", 2955, 5766, 2862, 5761),
                        new KLine("2010", 5803, 7521, 4586, 7067),
                        new KLine("2011", 7180, 7604, 5899, 5930),
                        new KLine("2012", 6020, 6862, 5113, 5549),
                        new KLine("2013", 5542, 5715, 4781, 4788),
                        new KLine("2014", 4787, 5133, 4163, 4619),
                        new KLine("2015", 4580, 5798, 4525, 5665),
                        new KLine("2016", 5663, 7314, 5284, 6826)
                )));
        symbol2ExtInfoMap.put("CF",
                new SymbolExtInfo("2004-06-01", Lists.newArrayList(
                        new KLine("2004", 16000, 16000, 11850, 12355),
                        new KLine("2005", 12325, 15735, 12250, 15285),
                        new KLine("2006", 15385, 15795, 13105, 14305),
                        new KLine("2007", 14180, 15660, 12955, 14805),
                        new KLine("2008", 14815, 16660, 10080, 11795),
                        new KLine("2009", 11995, 16715, 11530, 16520),
                        new KLine("2010", 16600, 33720, 15705, 28365),
                        new KLine("2011", 28250, 34870, 19850, 20690),
                        new KLine("2012", 20855, 22570, 18385, 18950),
                        new KLine("2013", 18940, 20550, 18310, 19345),
                        new KLine("2014", 19360, 20245, 12320, 13260),
                        new KLine("2015", 13040, 13640, 11280, 11400),
                        new KLine("2016", 11410, 17005, 9890, 15030)
                )));
        symbol2ExtInfoMap.put("OI",
                new SymbolExtInfo("2007-06-08", Lists.newArrayList(
                        new KLine("2007", 10380, 11100, 8342, 11000),
                        new KLine("2008", 11050, 15998, 5548, 6274),
                        new KLine("2009", 6470, 8676, 6342, 8516),
                        new KLine("2010", 8586, 10744, 7792, 10374),
                        new KLine("2011", 10440, 11184, 9230, 9510),
                        new KLine("2012", 9570, 10732, 9362, 9754),
                        new KLine("2013", 9788, 10134, 7032, 7034),
                        new KLine("2014", 7060, 7470, 5796, 6078),
                        new KLine("2015", 6002, 6246, 5430, 5702),
                        new KLine("2016", 5696, 7840, 5440, 7230)
                )));
        symbol2ExtInfoMap.put("PK",
                new SymbolExtInfo("2021-02-01", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("SF",
                new SymbolExtInfo("2014-08-08", Lists.newArrayList(
                        new KLine("2014", 5892, 5956, 5334, 5900),
                        new KLine("2015", 5320, 5442, 3366, 3780),
                        new KLine("2016", 3780, 5898, 3580, 5194)
                )));
        symbol2ExtInfoMap.put("SM",
                new SymbolExtInfo("2014-08-08", Lists.newArrayList(
                        new KLine("2014", 6560, 6694, 5940, 6130),
                        new KLine("2015", 6050, 6160, 3452, 3814),
                        new KLine("2016", 3750, 8734, 3750, 6912)
                )));
        symbol2ExtInfoMap.put("AP",
                new SymbolExtInfo("2017-12-22", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("CJ",
                new SymbolExtInfo("2019-04-30", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("RM",
                new SymbolExtInfo("2012-12-28", Lists.newArrayList(
                        new KLine("2012", 2430, 2458, 2336, 2340),
                        new KLine("2013", 2322, 2645, 2168, 2624),
                        new KLine("2014", 2616, 3118, 2067, 2253),
                        new KLine("2015", 2214, 2424, 1699, 1918),
                        new KLine("2016", 1911, 3003, 1824, 2308)
                )));
        symbol2ExtInfoMap.put("sc",
                new SymbolExtInfo("2018-03-26", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("lu",
                new SymbolExtInfo("2020-06-22", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("nr",
                new SymbolExtInfo("2019-08-12", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("bc",
                new SymbolExtInfo("2020-11-19", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("IF",
                new SymbolExtInfo("2010-04-16", Lists.newArrayList(
                        new KLine("2010", 3450, 3630, 2478.8, 3157),
                        new KLine("2011", 3171.8, 3395.8, 2293.2, 2357),
                        new KLine("2012", 2381, 2726, 2111.8, 2538),
                        new KLine("2013", 2565, 2830.2, 1996, 2345),
                        new KLine("2014", 2340, 3596.4, 2048.2, 3593.2),
                        new KLine("2015", 3619, 5400, 2686, 3658.2),
                        new KLine("2016", 3640, 3657.4, 2732.4, 3285)
                )));
        symbol2ExtInfoMap.put("IH",
                new SymbolExtInfo("2015-04-16", Lists.newArrayList(
                        new KLine("2015", 3100, 3565, 1781, 2388.8),
                        new KLine("2016", 2374, 2460.4, 1865.2, 2273.4)
                )));
        symbol2ExtInfoMap.put("IC",
                new SymbolExtInfo("2015-03-15", Lists.newArrayList(
                        new KLine("2015", 7681, 11585, 5101.8, 7349),
                        new KLine("2016", 7350, 7364.8, 5036, 6210.6)
                )));
        symbol2ExtInfoMap.put("IM",
                new SymbolExtInfo("2022-07-22", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("TS",
                new SymbolExtInfo("2018-08-17", Lists.newArrayList(
                )));
        symbol2ExtInfoMap.put("TF",
                new SymbolExtInfo("2013-09-16", Lists.newArrayList(
                        new KLine("2013", 94.22, 94.614, 90.8, 91.802),
                        new KLine("2014", 91.796, 98.072, 91.13, 96.772),
                        new KLine("2015", 96.77, 101.05, 95.12, 100.68),
                        new KLine("2016", 100.67, 102.29, 96.89, 99.4)
                )));
        symbol2ExtInfoMap.put("T",
                new SymbolExtInfo("2015-03-20", Lists.newArrayList(
                        new KLine("2015", 96.9, 100.59, 93.9, 100.035),
                        new KLine("2016", 100, 102.005, 93.48, 97.32)
                )));
        symbol2ExtInfoMap.put("si",
                new SymbolExtInfo("2022-12-22", Lists.newArrayList(
                )));
    }

    @Autowired
    private FutureSymbolCurrentPriceService futureSymbolCurrentPriceService;

    /**
     * 获取指定日期时间应该操作的品种
     *
     * @param datetime       yyyy-MM-dd HH:mm:ss
     * @param currentBalance 当前余额
     * @param excludeSymbols 排除哪些品种
     * @return key: 品种代码，而不是合约代码；value：开仓手数
     */
    public GridAndFixRatioSymbolInfo getTradeSymbol(String datetime, double currentBalance, String excludeSymbols) {
        Set<String> excludeSymbolSet = new HashSet<>();
        if (StringUtils.isNotBlank(excludeSymbols)) {
            excludeSymbolSet.addAll(Arrays.asList(excludeSymbols.split(",")));
        }
        int currentYear = Integer.parseInt(datetime.substring(0, 4));
        String currentDate = datetime.substring(0, 10);
        Map<String, GridAndFixRatioSymbolInfo> symbol2Info = new HashMap<>();
        Set<String> currentPositionSymbols = new HashSet<>(); // TODO 当前持仓品种
        Map<String, Double> runExchangeSymbol2PositionProfitRatioMap = new HashMap<>(); // TODO key: 正在操作中的品种; value: 当前品种需要盈利多少比例才可以止盈
        Map<String, Integer> runExchangeSymbol2PreMainContractPositionSlotMap = new HashMap<>(); // TODO key: 正在操作中的品种; value: 主力合约改变导致的平仓，需要记录一下当前主力合约持仓仓数
        Map<String, Map<Integer, Integer>> runExchangeSymbol2GridMap = new HashMap<>(); // TODO key: 正在操作中的品种; value: 当前合约所属网格

        for (String symbol : allowExchangeSymbols) {
            if (currentPositionSymbols.contains(symbol)) {
                continue;
            }
            if (!symbol2ExtInfoMap.containsKey(symbol) || currentDate.compareTo(symbol2ExtInfoMap.get(symbol).getStartDate()) < 0) {
                continue;
            }

            FutureSymbolCurrentPrice futureSymbolCurrentPrice = futureSymbolCurrentPriceService.getByLimitDatetime(symbol, datetime);
            if (futureSymbolCurrentPrice == null) {
                continue;
            }

            symbol2Info.put(symbol, new GridAndFixRatioSymbolInfo());
            symbol2Info.get(symbol).setSymbol(symbol);
            symbol2Info.get(symbol).setCurrentMainContract(futureSymbolCurrentPrice.getCurrentMainContract());
            symbol2Info.get(symbol).setCurrentPrice(futureSymbolCurrentPrice.getCurrentPrice());
            symbol2Info.get(symbol).setVolumeMultiple(futureSymbolCurrentPrice.getVolumeMultiple());
            SymbolExtInfo extInfo = symbol2ExtInfoMap.get(symbol);
            // 计算历史最高最低价格
            List<Double> allYearHighPriceList = new ArrayList<>();
            List<Double> allYearLowPriceList = new ArrayList<>();
            int fromYear = currentYear - HISTORY_YEAR_COUNT + 1;
            if (CollectionUtils.isNotEmpty(extInfo.getYearKline())) {
                for (KLine kLine : extInfo.getYearKline()) {
                    if (Integer.parseInt(kLine.getDatetime()) >= fromYear) {
                        allYearHighPriceList.add(kLine.getHigh());
                        allYearLowPriceList.add(kLine.getLow());
                    }
                }
            }
            if (StringUtils.isNotBlank(futureSymbolCurrentPrice.getYearInfo())) {
                // yearInfo格式如：{"2017": [12505.0, 17400.0, 8869381.0], "2018": [13520.0, 16040.0, 4233396.0], ...}
                Map<String, List<Number>> yearInfo = JSON.parseObject(futureSymbolCurrentPrice.getYearInfo(), Map.class);
                if (MapUtils.isNotEmpty(yearInfo)) {
                    for (String year : yearInfo.keySet()) {
                        if (Integer.parseInt(year) >= fromYear) {
                            allYearLowPriceList.add(yearInfo.get(year).get(0).doubleValue());
                            allYearHighPriceList.add(yearInfo.get(year).get(1).doubleValue());
                        }
                    }
                }
            }
            symbol2Info.get(symbol).setMinPrice(allYearLowPriceList.stream().min(Double::compareTo).orElse(0D));
            symbol2Info.get(symbol).setMaxPrice(allYearHighPriceList.stream().max(Double::compareTo).orElse(0D));
        }

        List<String> selectedSymbolList = new ArrayList<>();
        for (String symbol : symbol2Info.keySet()) {
            if (!currentPositionSymbols.contains(symbol)) {
                selectedSymbolList.add(symbol);
            }
        }

        selectedSymbolList.removeAll(excludeSymbolSet);

        if (selectedSymbolList.size() == 0) {
            return null;
        }

        logger.info("当前余额: " + currentBalance);

        for (String symbol : selectedSymbolList) {
            double currentPrice = symbol2Info.get(symbol).getCurrentPrice();
            double maybeMinPrice = symbol2Info.get(symbol).getMinPrice();
            double actualMaybeFallRatio = (currentPrice - maybeMinPrice) / currentPrice;
            double maybeFallRatio = Math.max(actualMaybeFallRatio, PARAM_MIN_FALL_RATIO);  // 迭代预计最低价格，需要跌的比例
            int gridCount = PARAM_GRID_COUNT; // 网格数
            int gridSize = (int) (currentPrice * maybeFallRatio / gridCount);  // 网格间距

            if (gridCount * (gridCount + 1) / 2.0 * gridSize * symbol2Info.get(symbol).getVolumeMultiple() > currentBalance * PARAM_MAX_LOSS_RATIO) {
                // 即使网格只有1手，可能亏损额度也超过最大允许比例，那么就不开这个品种
                continue;
            }

            int gridSlot = Integer.max((int) (currentBalance * PARAM_MAX_LOSS_RATIO / (((gridCount * (gridCount - 1) / 2) + gridCount) * gridSize * symbol2Info.get(symbol).getVolumeMultiple())), 1);  // 一格多少手
            symbol2Info.get(symbol).setOpenSlot(gridSlot);
            DecimalFormat df = new DecimalFormat();
            df.setMaximumFractionDigits(2);
            symbol2Info.get(symbol).setActualMaybeFallRatio(Double.parseDouble(df.format(actualMaybeFallRatio)));
            symbol2Info.get(symbol).setGridCount(gridCount);
            symbol2Info.get(symbol).setMaybeFallRatio(Double.parseDouble(df.format(maybeFallRatio)));
            Map<Integer, Integer> price2GridSlotMap = new LinkedHashMap<>();
            price2GridSlotMap.put((int) currentPrice, gridSlot);
            symbol2Info.get(symbol).setGrid(price2GridSlotMap);
            for (int i = 1; i < gridCount * 5; i++) {
                int price = (int) (currentPrice - gridSize * i);
                if (price > 0) {
                    price2GridSlotMap.put(price, gridSlot + i * gridSlot);
                }
            }
        }

        for (String symbol : runExchangeSymbol2PositionProfitRatioMap.keySet()) {
            if (!currentPositionSymbols.contains(symbol) && symbol2Info.containsKey(symbol)) { // 正在操作但是目前没有持仓，说明因为换期需要移仓
                logger.info("未达到盈利目标，继续持仓: " + symbol + ", 还需要盈利比例: " + runExchangeSymbol2PositionProfitRatioMap.get(symbol));
                GridAndFixRatioSymbolInfo result = symbol2Info.get(symbol);
                result.setOpenSlot(runExchangeSymbol2PreMainContractPositionSlotMap.get(symbol));
                return result;
            }
        }

        String selectedSymbol = null;
        for (String symbol : symbol2Info.keySet()) {
            GridAndFixRatioSymbolInfo info = symbol2Info.get(symbol);
            if (MapUtils.isEmpty(info.getGrid())) {
                continue;
            }
            if (selectedSymbol == null) {
                selectedSymbol = symbol;
                continue;
            }
            if (info.getOpenSlot() > symbol2Info.get(selectedSymbol).getOpenSlot()) {
                selectedSymbol = symbol;
                continue;
            }
            if (info.getOpenSlot() == symbol2Info.get(selectedSymbol).getOpenSlot()
                    && info.getMaybeFallRatio() - symbol2Info.get(selectedSymbol).getMaybeFallRatio() <= -0.01) {
                selectedSymbol = symbol;
                continue;
            }
            if (info.getOpenSlot() == symbol2Info.get(selectedSymbol).getOpenSlot()
                    && Math.abs(info.getMaybeFallRatio() - symbol2Info.get(selectedSymbol).getMaybeFallRatio()) < 0.01
                    && info.getActualMaybeFallRatio() > symbol2Info.get(selectedSymbol).getActualMaybeFallRatio()) {
                selectedSymbol = symbol;
                continue;
            }
        }

        if (selectedSymbol == null) {
            return null;
        }

        runExchangeSymbol2GridMap.put(selectedSymbol, symbol2Info.get(selectedSymbol).getGrid());
        runExchangeSymbol2PositionProfitRatioMap.put(selectedSymbol, PARAM_POSITION_PROFIT_RATIO);

        return symbol2Info.get(selectedSymbol);
    }
}
